#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os

import psutil

from common.common import execute_cmd
from common.const import IPConstant, CMDResult
from common.exception.common_exception import ErrCodeException
from generaldb.gbase8a import LOGGER
from generaldb.gbase8a.common.const import ErrorCode, RoachConstant, GbaseEnvName, GbasePath, JsonConstant, NormalErr


class GbaseUtil:

    @staticmethod
    def get_agent_id(param):
        target_agent_id = param.get_local_agent_id()
        source_node_map = param.get_source_node_map()
        source_map = dict(sorted(source_node_map.items(), key=lambda x: x[1]))
        if target_agent_id in source_map.keys():
            return target_agent_id
        target_map = dict(sorted(param.get_target_node_map().items(), key=lambda x: x[1]))
        if len(source_map) != len(source_map) or list(source_map.values()) != list(target_map.values()):
            LOGGER.error("The count of nodes is different. source:%s, target:%s", source_map, target_map)
            return ErrorCode.ERROR_DIFFERENT_GBASE_TOPO
        target_index = list(target_map.keys()).index(target_agent_id)
        return list(source_map.keys())[target_index]

    @staticmethod
    def parse_custom_str_to_dict(custom_str) -> dict:
        """
        转换自定义参数为字典
        :param custom_str: "field=value,field1=value1,……”
        :return: {field:value,field1:value,……}
        """
        custom_list = [kv_str.split("=", 1) for kv_str in custom_str.split(",")]
        custom_dict = {kv_list[0]: kv_list[1] for kv_list in custom_list if len(kv_list) > 1}
        return custom_dict

    @staticmethod
    def extract_ip():
        """
        获取当前主机所有ip
        :return: list，主机ip
        """
        LOGGER.info("Start getting all local ips ...")
        local_ips = []
        try:
            ip_dict = psutil.net_if_addrs()
        except Exception as err:
            LOGGER.error("Get ip address err: %s.", err)
            return local_ips
        for _, info_list in ip_dict.items():
            for i in info_list:
                if i[0] == RoachConstant.ADDRESS_FAMILY_AF_INET and i[1] != IPConstant.LOCAL_HOST:
                    local_ips.append(i[1])
        return local_ips

    @staticmethod
    def translate_to_snake_name(camel_name: str) -> str:
        def latin(x):
            return f'_{chr(x + 32)}'

        trans_map = {chr(key): latin(key) for key in range(65, 91)}
        tran_map = camel_name.maketrans(trans_map)
        method = camel_name.translate(tran_map)
        return method.lstrip("_")

    @staticmethod
    def get_gcrcman_path(cmd, os_user):
        ret, gcluster_base_path = cmd.get_db_os_env(os_user, GbaseEnvName.GCLUSTER_BASE)
        if not ret:
            LOGGER.error("Failed to get gcrcman path")
            raise ErrCodeException(err_code=ErrorCode.ERROR_NO_GCRCMAN, message='Failed to get gcrcman path')
        return os.path.join(gcluster_base_path.strip(), GbasePath.GCRCMAN_PATH)

    @staticmethod
    def has_pexpect(os_user):
        cmd = f'su - {os_user} -c "python -c \'import pexpect\'"'
        ret, out, err = execute_cmd(cmd)
        return ret == CMDResult.SUCCESS.value

    @staticmethod
    def get_current_node_ip(param_inst):
        result_param = param_inst.get_param()
        nodes = result_param.get(JsonConstant.APP_ENV, {}).get(JsonConstant.NODES, [])
        end_points = [node.get(JsonConstant.ENDPOINT, '') for node in nodes]
        local_ip_list = GbaseUtil.extract_ip()
        if not end_points:
            return local_ip_list[0]
        for local_ip in local_ip_list:
            if local_ip in end_points:
                return local_ip
        return local_ip_list[0]

    @staticmethod
    def check_service_status(sys_user):
        code, param = GbaseUtil.check_cluster_service(sys_user, RoachConstant.ALL_SERVICE_INFO)
        if code != NormalErr.NO_ERR:
            return False
        code, param = GbaseUtil.check_cluster_service(sys_user, RoachConstant.ALL_GCWARE_INFO)
        return code == NormalErr.NO_ERR

    @staticmethod
    def check_cluster_service(user, cmd_str):
        cmd = f'su - {user} -c "{cmd_str}"'
        return_code, out_info, err_info = execute_cmd(cmd)
        if return_code != CMDResult.SUCCESS:
            LOGGER.error("code:%s, out:%s, err:%s", return_code, out_info, err_info)
            if "command not found" in out_info or "command not found" in err_info:
                LOGGER.warn("No command:%s", cmd_str)
                return NormalErr.NO_ERR, []
            LOGGER.error("Get all service error!")
            return ErrorCode.ERR_DB_SERVICES, []
        for line in out_info.split("\n"):
            if line and "running" not in line:
                return ErrorCode.ERR_DB_SERVICES, []
        return NormalErr.NO_ERR, []
